﻿using System;
using System.Collections.Generic;
using System.Text;
using Marx.Utils;

namespace Marx
{
    public class TimerSystem
    {
        private Dictionary<int, Timer> timers = new Dictionary<int, Timer>();
        private SortedList<long, Timer> invokers = new SortedList<long, Timer>();
        private int idStart = 0;
        private long minTimestamps = long.MaxValue;

        public int CreateTimer(Action<int> callback, long delay, long interval = long.MaxValue)
        {
            ++idStart;
            Timer timer = new Timer();
            timer.ID = idStart;
            timer.Method += callback;
            timer.Delay = delay;
            timer.Interval = interval;
            timer.Timestamps = TimeUtils.CurUnixTimestamp;

            if (delay == 0)
            {
                timer.Invoke();
                if (interval == long.MaxValue)
                {
                    return 0;
                }
            }

            timers.Add(timer.ID, timer);
            if (timer.NextTriggerTime < minTimestamps)
            {
                minTimestamps = timer.NextTriggerTime;
            }
            return timer.ID;
        }

        public int CreateTimerTill(Action<int> callback, long time)
        {
            var id = 0;
            var delta = time - TimeUtils.CurUnixTimestamp;
            if (delta <= 0)
            {
                id = ++idStart;
                callback(id);
            }
            else
            {
                id = CreateTimer(callback, delta);
            }
            return id;
        }

        public void RemoveTimer(int id)
        {
            if (id <= 0)
            {
                return;
            }

            timers.Remove(id);
        }

        public void Clear()
        {
            invokers.Clear();
            timers.Clear();
            idStart = 0;
            minTimestamps = long.MaxValue;
        }

        public void Update()
        {
            long curTime = TimeUtils.CurUnixTimestamp;

            if (curTime >= minTimestamps)
            {
                minTimestamps = long.MaxValue;
                invokers.Clear();
                foreach (var timer in timers)
                {
                    long nextTime = timer.Value.NextTriggerTime;
                    if (curTime >= nextTime)
                    {
                        invokers.Add(nextTime, timer.Value);
                    }
                    else
                    {
                        if (nextTime < minTimestamps)
                        {
                            minTimestamps = nextTime;
                        }
                    }
                }

                if (invokers.Count > 0)
                {
                    foreach (var pair in invokers)
                    {
                        var invoker = pair.Value;
                        invoker.Invoke();
                        if (invoker.Expire)
                        {
                            RemoveTimer(invoker.ID);
                        }
                        else
                        {
                            long nextTime = invoker.NextTriggerTime;
                            if (nextTime < minTimestamps)
                            {
                                minTimestamps = nextTime;
                            }
                        }
                    }

                    invokers.Clear();
                }
            }
        }
    }
}
